home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Classes / 2.0_PetriDish / PetriDish / PetriDish.m < prev    next >
Encoding:
Text File  |  1992-07-02  |  12.7 KB  |  438 lines

  1. // PetriDish.m
  2. // By Charles G. Fleming, Educational Computing Services, Allegheny College.
  3. // You may freely copy, distribute and reuse this code. 
  4. // Allegheny College and the author disclaim any warranty of any kind, 
  5. // expressed or implied, as to its fitness for any particular use.
  6. // This work was partially supported by a grant from the Vira Heinz Endowment.
  7.  
  8. #import "PetriDish.h"
  9. #import "DrawPetriDish.h"
  10.  
  11. #import <appkit/color.h>
  12. #import <appkit/NXImage.h>
  13. #import <appkit/Window.h>
  14. #import <appkit/TextField.h>
  15. #import <appkit/NXImage.h>
  16. #import <appkit/NXCursor.h>
  17. #import <appkit/View.h>
  18.  
  19. #import <dpsclient/wraps.h>
  20.  
  21. #import <stdlib.h>
  22. #import <math.h>
  23. #import <libc.h>
  24.  
  25. #define    ONECENTIMETER  28.3465                              // Assuming 72 dpi.
  26. #define    ONEMILLIMETER   2.83465;
  27.  
  28. @implementation PetriDish
  29.  
  30. - (const char *)inspectorName
  31. {
  32.     return "PetriDishInspector";
  33. }
  34.     
  35. // This is called when the view is resized in Interface Builder and when the OK button is pushed. 
  36. // We will force the shape to be square.  Redraw the petri dish in a new NXImage, then display it.
  37. - setFrame:(const NXRect *)frameRect
  38. {
  39.     NXRect rect;
  40.     float side;
  41.  
  42.     // Force the view to be a square.
  43.     if(frameRect->size.width < frameRect->size.height)
  44.         side = frameRect->size.width;
  45.     else
  46.         side = frameRect->size.height;
  47.     
  48.     NXSetRect(&rect, frameRect->origin.x, frameRect->origin.y, side, side);    
  49.      self = [super setFrame:&rect];
  50.   
  51.     // Free the old NXImage and set up a new one.
  52.     if(!okButtonPushed)
  53.     {    
  54.         agarRadius = 0.90 * (bounds.size.width/2.0);   // in pixels
  55.         petriDishRadius = 0.0352778 * agarRadius;    // in centimeters  (using 72 dpi)
  56.         [petriDishNXImage free];    
  57.         petriDishNXImage = [[NXImage alloc] initSize:&bounds.size];
  58.         [petriDishNXImage lockFocus];
  59.         drawPetriDish(bounds.size.width, bounds.size.width/2.0, agarRadius, 
  60.                            petriRed, petriGreen, petriBlue);
  61.         [petriDishNXImage unlockFocus];
  62.     
  63.         [gridNXImage free];    
  64.         gridNXImage = [[NXImage alloc] initSize:&bounds.size];
  65.         [gridNXImage lockFocus];
  66.         drawGrid(bounds.size.width, bounds.size.width/2.0, agarRadius, gridDelta);
  67.         [gridNXImage unlockFocus];
  68.  
  69.         [spotsNXImage free];    
  70.         spotsNXImage = [[NXImage alloc] initSize:&bounds.size];
  71.     if(numSpots)
  72.         [self drawSpotsInNXImage:numSpots];
  73.         [self display];        
  74.     }        
  75.  
  76.     if(inspector != nil)
  77.         [inspector perform:@selector(revert:) with:self];   
  78.     okButtonPushed = NO;
  79.     return self;
  80. }   
  81.  
  82. - initFrame:(NXRect *)frameRect
  83. {
  84.     NXSize cursorSize = {16.0, 16.0};
  85.     
  86.     self = [super initFrame:frameRect];
  87.  
  88.     // Initialize the instance variables.    
  89.     gridState = NO;
  90.     gridSpacing = 1.0;                                             // centimeters
  91.     agarRadius = 0.90 * (bounds.size.width/2.0);   // in pixels
  92.     petriDishRadius = 0.0352778 * agarRadius;    // in centimeters  (using 72 dpi)
  93.     petriRed = 1.0;
  94.     petriGreen = 1.0;
  95.     petriBlue = 0.4902; 
  96.     agarColor = NXConvertRGBAToColor(petriRed, petriGreen, petriBlue, 1.0);
  97.     spotRed = 0.0;
  98.     spotGreen = 0.0;
  99.     spotBlue = 0.0;
  100.     spotColor = NXConvertRGBAToColor(spotRed, spotGreen, spotBlue, 1.0);
  101.     numSpots = 0;
  102.      
  103.     petriDishNXImage = [[NXImage alloc] initSize:&bounds.size];
  104.     [petriDishNXImage lockFocus];
  105.     drawPetriDish(bounds.size.width, bounds.size.width/2.0, agarRadius, 
  106.                            petriRed, petriGreen, petriBlue);
  107.     [petriDishNXImage unlockFocus];
  108.  
  109.     spotRadius = 5;                                                      // In millimeters.
  110.     dotRadius = spotRadius * ONEMILLIMETER;       // in pixels
  111.     gridDelta = ONECENTIMETER;                             // in pixels
  112.     
  113.     gridNXImage = [[NXImage alloc] initSize:&bounds.size];
  114.     [gridNXImage lockFocus];
  115.     drawGrid(bounds.size.width, bounds.size.width/2.0, agarRadius, gridDelta);
  116.     [gridNXImage unlockFocus];
  117.  
  118.     spotsNXImage = [[NXImage alloc] initSize:&bounds.size];
  119.  
  120.     spotNXImageSize.width = 2 * dotRadius + 2.0;
  121.     spotNXImageSize.height = 2 * dotRadius + 2.0;
  122.     spotNXImage = [[NXImage alloc] initSize:&spotNXImageSize];
  123.     [spotNXImage lockFocus];    
  124.     drawSpot(spotNXImageSize.width, spotNXImageSize.width/2.0, dotRadius, 
  125.                  spotRed, spotGreen, spotBlue);
  126.     [spotNXImage unlockFocus];
  127.  
  128.     okButtonPushed = NO;
  129.     
  130.     // Set up the cursor.
  131.     clickCursorNXImage = [[NXImage alloc] initSize:&cursorSize];
  132.     [clickCursorNXImage lockFocus];
  133.     drawCursor();
  134.     [clickCursorNXImage unlockFocus];
  135.     
  136.     clickCursor = [[NXCursor allocFromZone:[self zone]]     
  137.             initFromImage:clickCursorNXImage];
  138.     return self;
  139. }    
  140.  
  141. // Turn grid on or off.  If it is on, draw it with the specified spacing.  Composites the grid
  142. // onto the petri dish.
  143. - turnGridOn:(BOOL)flag withSpacing:(float)spacing
  144. {
  145.     gridState = flag;
  146.     if(gridState)
  147.     {
  148.         gridSpacing = spacing;                                 // in centimeters
  149.         gridDelta = spacing * ONECENTIMETER;           // in pixels
  150.         [gridNXImage lockFocus];
  151.         drawGrid(bounds.size.width, bounds.size.width/2.0, agarRadius, gridDelta);
  152.         [gridNXImage unlockFocus];
  153.     }
  154.     [self display];
  155.     return self;
  156. }
  157.  
  158. // Set the radius of the agar.  Draws the petriDishNXImage.  Does not redraw the petri dish.    
  159. - setAgarRadius:(float)radius
  160. {
  161.     petriDishRadius = radius;                                  // in centimeters
  162.     agarRadius = radius * ONECENTIMETER;      // in pixels
  163.     if(agarRadius >bounds.size.width / 2.0)
  164.     {
  165.         agarRadius = bounds.size.width / 2.0; 
  166.         petriDishRadius = agarRadius / ONECENTIMETER;
  167.     }       
  168.  
  169.     [petriDishNXImage lockFocus];
  170.     drawPetriDish(bounds.size.width, bounds.size.width/2.0, agarRadius, 
  171.                            petriRed, petriGreen, petriBlue);
  172.     [petriDishNXImage unlockFocus];
  173.     
  174.     // Draw the spots NXImage.
  175.     if(numSpots)
  176.         [self drawSpotsInNXImage:numSpots];    
  177.  
  178.     // Draw the grid NXImage.
  179.     if(gridState)
  180.     {
  181.         [gridNXImage lockFocus];
  182.         drawGrid(bounds.size.width, bounds.size.width/2.0, agarRadius, gridDelta);
  183.         [gridNXImage unlockFocus];
  184.     }    
  185.     return self;
  186. }
  187.  
  188. // Set the color of the agar.  Draws the petriDishNXImage.  Does not redraw the petri dish.    
  189. - setAgarColor:(NXColor)color
  190. {
  191.     float alpha = 1.0;
  192.  
  193.     agarColor = color;
  194.     NXConvertColorToRGBA(agarColor, &petriRed, &petriGreen, &petriBlue, &alpha);
  195.     
  196.     [petriDishNXImage lockFocus];
  197.     drawPetriDish(bounds.size.width, bounds.size.width/2.0, agarRadius, 
  198.                            petriRed, petriGreen, petriBlue);
  199.     [petriDishNXImage unlockFocus];
  200.     return self;
  201. }
  202.  
  203. // Set the color of the spots.  Redraws the spotNXImage and spotsNXImage. 
  204. // Does not redraw the petri dish.  
  205. - setSpotColor:(NXColor)color
  206. {
  207.     float alpha = 1.0;
  208.  
  209.     spotColor = color;
  210.     NXConvertColorToRGBA(spotColor, &spotRed, &spotGreen, &spotBlue, &alpha);
  211.  
  212.     // Draw the spot NXImage.
  213.     [spotNXImage lockFocus];    
  214.     drawSpot(spotNXImageSize.width, spotNXImageSize.width/2.0, dotRadius, 
  215.                  spotRed, spotGreen, spotBlue);
  216.     [spotNXImage unlockFocus];    
  217.  
  218.     // Draw the spots NXImage.
  219.     if(numSpots)
  220.         [self drawSpotsInNXImage:numSpots];    
  221.     return self;
  222. }
  223.  
  224. // Set the radius of the spots.  Redraws the spotNXImage and spotsNXImage. 
  225. // Does not redraw the petri dish.  
  226. - setSpotRadius:(float)radius
  227. {
  228.     spotRadius = radius;                                               // In millimeters.
  229.     dotRadius = spotRadius * ONEMILLIMETER;        // In pixels.
  230.     
  231.     if(dotRadius > 0.5 * agarRadius)
  232.     {
  233.         dotRadius = 0.5 * agarRadius;            
  234.         spotRadius = dotRadius / ONEMILLIMETER;
  235.     }
  236.             
  237.     spotNXImageSize.width = 2 * dotRadius + 2.0;
  238.     spotNXImageSize.height = 2 * dotRadius + 2.0;
  239.     [spotNXImage free];    
  240.     spotNXImage = [[NXImage alloc] initSize:&spotNXImageSize];
  241.  
  242.     // Draw the spot NXImage.
  243.     [spotNXImage lockFocus];    
  244.     drawSpot(spotNXImageSize.width, spotNXImageSize.width/2.0, dotRadius, 
  245.                  spotRed, spotGreen, spotBlue);
  246.     [spotNXImage unlockFocus];
  247.     
  248.     // Draw the spots NXImage.
  249.     if(numSpots)
  250.         [self drawSpotsInNXImage:numSpots];    
  251.     return self;
  252. }
  253.  
  254. // Draws the spotNXImage and the spots spotsNXImage.  Does not redraw the petri dish.
  255. - setNumSpots:(int)spots
  256. {
  257.     numSpots = spots;
  258.     
  259.     // Draw the spots NXImage.
  260.     [self drawSpotsInNXImage:numSpots];
  261.     return self;
  262. }        
  263.  
  264. // Draws the petri dish and the spots and grid if necessary.    
  265. - drawSelf:(NXRect *)rects :(int)count
  266. {
  267.     NXPoint origin = {0.0, 0.0};
  268.  
  269.     [petriDishNXImage composite:NX_COPY toPoint:&origin];
  270.      
  271.     if(numSpots)
  272.           [spotsNXImage composite:NX_SOVER toPoint:&origin];
  273.  
  274.     if(gridState)
  275.         [gridNXImage composite:NX_SOVER toPoint:&origin];   
  276.     return self;
  277. }
  278.  
  279. - drawPetriDishWithSpots:(int)spots
  280. {
  281.     [self setNumSpots:spots];
  282.     [self display];
  283.     return self;
  284. }
  285.  
  286. // Draws the spots in an NXImage for the petri dish.  Does not redraw the petri dish.    
  287. - drawSpotsInNXImage:(int)spots;
  288. {
  289.     float maxRadius, halfWidth, randomRadius, randomAngle, spotHalfWidth ;
  290.     NXPoint origin;
  291.     int spot; 
  292.  
  293.     // Composite spots into spotsNXImage.        
  294.     [spotsNXImage lockFocus];
  295.     PSsetalpha(0.0);
  296.     NXRectFill(&bounds);
  297.     PSsetalpha(1.0);
  298.  
  299.     maxRadius = agarRadius - dotRadius;
  300.     halfWidth = bounds.size.width / 2.0;
  301.     spotHalfWidth = spotNXImageSize.width/2.0;
  302.     
  303.     numSpots = spots;
  304.     for (spot = 0; spot < numSpots; spot++)
  305.     {    
  306.         randomRadius =  maxRadius * random() / 2147483647.0;
  307.         randomAngle =  6.28319 * random() / 2147483647.0;
  308.         origin.x = randomRadius * cos(randomAngle) + halfWidth - spotHalfWidth;
  309.         origin.y = randomRadius * sin(randomAngle) + halfWidth - spotHalfWidth;
  310.         [spotNXImage composite:NX_SOVER toPoint:&origin];
  311.     } 
  312.     [spotsNXImage unlockFocus];   
  313.      return self;
  314. }    
  315.  
  316. - (float)gridSpacing
  317. {
  318.     return gridSpacing;        // In centimeters
  319. }   
  320.  
  321. - (BOOL)gridState
  322. {
  323.     return gridState;
  324. }
  325.  
  326. - (float)petriDishRadius
  327. {
  328.     return petriDishRadius;   // In centimeters
  329. }  
  330.  
  331. - (int)numSpots
  332. {
  333.     return numSpots;
  334. }  
  335.  
  336. - (float)spotRadius
  337. {
  338.     return spotRadius;         // In millimeters.
  339. }  
  340.  
  341. - okPushed:(BOOL)flag
  342. {
  343.     okButtonPushed = flag;
  344.     return self;
  345.  
  346. - setInspector:anObject
  347. {
  348.     inspector = anObject;
  349.     return self;
  350. }    
  351.  
  352. - (NXColor) agarColor
  353. {
  354.     return agarColor;
  355. }       
  356.  
  357. - (NXColor) spotColor
  358. {
  359.     return spotColor;
  360. }       
  361.  
  362. // Increment the count each time we click in the petri dish.     
  363. - mouseDown:(NXEvent *)theEvent
  364. {
  365.     clicks++;
  366.     if(clickCountTextField)
  367.         [clickCountTextField setIntValue:clicks];
  368.     return self;
  369. }
  370.  
  371. // Set the count back to zero.
  372. - resetClickCount:sender
  373. {
  374.     clicks = 0;
  375.     if(clickCountTextField)
  376.         [clickCountTextField setIntValue:clicks];
  377.     return self;
  378. }    
  379.  
  380. - (int)clickCount
  381. {
  382.     return clicks;
  383. }    
  384.  
  385. - resetCursorRects
  386. {
  387.     [self addCursorRect:&bounds cursor:clickCursor];
  388.     return self;
  389. }
  390.  
  391. - write:(NXTypedStream *)typedStream
  392. {
  393.     [super write:typedStream];
  394.     NXWriteObject(typedStream, petriDishNXImage);
  395.     NXWriteObject(typedStream, gridNXImage);
  396.     NXWriteObject(typedStream, spotsNXImage);
  397.     NXWriteObject(typedStream, spotNXImage);
  398.     NXWriteObject(typedStream, clickCountTextField);
  399.     NXWriteObject(typedStream, clickCursor);
  400.     NXWriteSize(typedStream, &spotNXImageSize);
  401.     NXWriteTypes(typedStream, "{if}", &gridState, &gridSpacing);
  402.     NXWriteTypes(typedStream, "{ff}", &agarRadius, &petriDishRadius);
  403.     NXWriteTypes(typedStream, "{fff}", &petriRed, &petriGreen, &petriBlue);
  404.     NXWriteTypes(typedStream, "{fff}", &spotRed, &spotGreen, &spotBlue);
  405.     NXWriteTypes(typedStream, "{ii}", &numSpots, &okButtonPushed);
  406.     NXWriteTypes(typedStream, "{ff}", &spotRadius, &dotRadius);
  407.     return self;
  408. }
  409.              
  410. - read:(NXTypedStream *)typedStream
  411. {
  412.     [super read:typedStream];
  413.     petriDishNXImage = NXReadObject(typedStream);  
  414.     gridNXImage = NXReadObject(typedStream);
  415.     spotsNXImage = NXReadObject(typedStream);  
  416.     spotNXImage = NXReadObject(typedStream);
  417.     clickCountTextField = NXReadObject(typedStream);
  418.     clickCursor = NXReadObject(typedStream);
  419.     NXReadSize(typedStream, &spotNXImageSize);
  420.     NXReadTypes(typedStream, "{if}", &gridState, &gridSpacing);
  421.     NXReadTypes(typedStream, "{ff}", &agarRadius, &petriDishRadius);
  422.     NXReadTypes(typedStream, "{fff}", &petriRed, &petriGreen, &petriBlue);
  423.     NXReadTypes(typedStream, "{fff}", &spotRed, &spotGreen, &spotBlue);
  424.     NXReadTypes(typedStream, "{ii}", &numSpots, &okButtonPushed);
  425.     NXReadTypes(typedStream, "{ff}", &spotRadius, &dotRadius);
  426.     return self;
  427. }
  428.  
  429. - awake
  430. {
  431.     agarColor = NXConvertRGBAToColor(petriRed, petriGreen, petriBlue, 1.0);
  432.     spotColor =    NXConvertRGBAToColor(spotRed, spotGreen, spotBlue, 1.0);
  433.     clicks = 0;
  434.     return self;
  435. }
  436.     
  437. @end